using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

public class Vertex
{
    public Vector3 m_Position;
    public Vector3 m_Normal;
    public Vector2 m_Uv;
    public int m_Activated;    //Property for quadtree only 
    public int m_Index;

    public List<Quad> m_Neighbours = new List<Quad>();

   

    public List<Face>m_Faces = new List<Face>();

    public Vertex(Vector3 position, Vector2 uv, int index)
    {
        m_Position = position;
        m_Uv = uv;
        m_Index=index;
    }

    public Vertex(Vector3 position, Vector3 normal, Vector2 uv)
    {
        m_Position=position;
        m_Normal=normal;
        m_Uv=uv;
    }

    public void ComputeNormal()
    {
        Vector3 sum= new Vector3();

        for (int i = 0; i < m_Faces.Count; i++)
        {
            sum += m_Faces[i].m_Normal;
        }
        sum = (sum) / (m_Faces.Count);
        m_Normal = sum.normalized;
        
    }
}

public class Face
{
    public Vector3 m_Normal;

    public Vertex[] m_Vertices= new Vertex[3];

    public Face( Vertex a, Vertex b, Vertex c )
    {
        m_Vertices[0]=a;
        m_Vertices[1]=b;
        m_Vertices[2]=c;
    }

    public void ComputNormal()
    {
        m_Normal = Vector3.Cross(m_Vertices[0].m_Position - m_Vertices[1].m_Position, m_Vertices[1].m_Position - m_Vertices[2].m_Position);
        m_Normal.Normalize();
    }
}

public class Heightmap : MonoBehaviour
{
    public      float           m_pow;
    public      float           m_Radius;
    public      bool            m_Sphere;
    public      int             m_OctaveCount;
    public      float           m_Interval;
    public      int             m_Value;
    public      int             m_Seed;
    public      Texture2D       m_Heightmap;

    public      Vertex[]        m_Vertices;
    public      Face[]          m_Triangles;


    public float m_StepX;
    public float m_StepY;


    public void CreateMesh()
    {
        Mesh m_Mesh = new Mesh();


        m_Vertices = new Vertex[m_Heightmap.height * m_Heightmap.width];
        m_Triangles = new Face[(((m_Heightmap.height - 1) * (m_Heightmap.width - 1)) * 2)];


        // First I build the vertex and theirs attributes

        for (int i = 0; i < m_Heightmap.height; i++)
        {
            for (int j = 0; j < m_Heightmap.width; j++)
            {
                Vertex vertex = new Vertex(
                                                new Vector3(j * m_StepX, (m_Heightmap.GetPixel(i, j).grayscale) * 10.0f, i * m_StepY),
                                                new Vector2(i, j), i * m_Heightmap.width + j
                                            );
                m_Vertices[i * m_Heightmap.width + j] = vertex;
            }
        }


        //Then I build the triangles
        for (int i = 0; i < m_Heightmap.height - 1; i++)
        {
            for (int j = 0; j < m_Heightmap.width - 1; j++)
            {
                m_Triangles[(i * (m_Heightmap.width - 1) * 2) + (j * 2) + 0] = new Face(m_Vertices[(((i + 1) * m_Heightmap.width) + j + 1)],
                    m_Vertices[(((i) * m_Heightmap.width) + j + 1)],
                    m_Vertices[(((i) * m_Heightmap.width) + j)]);

                m_Vertices[(((i + 1) * m_Heightmap.width) + j + 1)].m_Faces.Add(
                            m_Triangles[(i * (m_Heightmap.width - 1) * 2) + (j * 2) + 0]);
                m_Vertices[(((i) * m_Heightmap.width) + j + 1)].m_Faces.Add(m_Triangles[(i * (m_Heightmap.width - 1) * 2) + (j * 2) + 0]);
                m_Vertices[(((i) * m_Heightmap.width) + j)].m_Faces.Add(m_Triangles[(i * (m_Heightmap.width - 1) * 2) + (j * 2) + 0]);

                m_Triangles[(i * (m_Heightmap.width - 1) * 2) + (j * 2) + 1] = new Face(m_Vertices[i * m_Heightmap.width + j],
                    m_Vertices[((i + 1) * m_Heightmap.width) + j],
                    m_Vertices[((i + 1) * m_Heightmap.width) + j + 1]);

                m_Vertices[i * m_Heightmap.width + j].m_Faces.Add(m_Triangles[(i * (m_Heightmap.width - 1) * 2) + (j * 2) + 1]);
                m_Vertices[((i + 1) * m_Heightmap.width) + j].m_Faces.Add(m_Triangles[(i * (m_Heightmap.width - 1) * 2) + (j * 2) + 1]);
                m_Vertices[((i + 1) * m_Heightmap.width) + j + 1].m_Faces.Add(m_Triangles[(i * (m_Heightmap.width - 1) * 2) + (j * 2) + 1]);

            }
        }

        if (m_Sphere)
        {
            MapToSphere();
        }

      

        GetComponent<MeshFilter>().mesh = m_Mesh;

        ComputeFaceNormals();
        ComputeNormals();
        

        m_Mesh.vertices = ReturnVertexPosition();
        m_Mesh.uv = ReturnVertexUv();
        m_Mesh.normals = ReturnVertexNormal();
        m_Mesh.triangles = ReturnIndices();

    }
	// Use this for initialization
	void Start () 
    {

        CreateMesh();
	}

  
    public void ComputeFaceNormals()
    {

        for (int i = 0; i < m_Triangles.Length; i++)
        {
            m_Triangles[i].ComputNormal();
        }
    }

    public void ComputeNormals()
    {
        for (int i = 0; i < m_Vertices.Length; i++)
        {
            m_Vertices[i].ComputeNormal();
        }
    }

    int[] ReturnIndices()
    {
        int[] indices = new int[m_Triangles.Length*3];

            for ( int i = 0; i < m_Triangles.Length; i++)
            {
                indices[i * 3 + 0] = m_Triangles[i].m_Vertices[0].m_Index;
                indices[i * 3 + 1] = m_Triangles[i].m_Vertices[1].m_Index;
                indices[i * 3 + 2] = m_Triangles[i].m_Vertices[2].m_Index;
            }

        return indices;

    }

    public Vector3[] ReturnVertexPosition()
    {
        Vector3 []vertices = new Vector3[m_Heightmap.height*m_Heightmap.width];

        for (int i = 0; i < m_Vertices.Length; i++)
        {
            vertices[i] = m_Vertices[i].m_Position;
        }

        return vertices;
        
    }

Vector3[] ReturnVertexNormal()
{
  
    Vector3[] vertices = new Vector3[m_Heightmap.height * m_Heightmap.width];

    for (int i = 0; i < m_Vertices.Length; i++)
    {
        
       
            vertices[i] = m_Vertices[i].m_Normal;
        
    }

    return vertices;
}
Vector2[] ReturnVertexUv()
{
    Vector2[] vertices = new Vector2[m_Heightmap.height * m_Heightmap.width];

    for (int i = 0; i < m_Vertices.Length; i++)
    {
        vertices[i] = m_Vertices[i].m_Uv;
    }

    return vertices;
}

    // Update is called once per frame
    void Update() 
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            this.m_Heightmap = RandomTexture.CreateTexture(128, 128, m_OctaveCount, m_Interval, m_Value, m_Seed);
            CreateMesh();
            m_Seed += 1;
        }
	}

    void MapToSphere()
    {
        float stepX =    m_Heightmap.width;
        float stepY =   m_Heightmap.height;

        float stepTeta = (2.0f*Mathf.PI) / (stepX-1);
        float stepPhi = Mathf.PI / (stepY-1);

        float startPhi = -Mathf.PI / 2.0f;
        float startTeta = -Mathf.PI;

        float sumBottom=0.0f;
        float sumTop = 0.0f;

        for (int i = 0; i < m_Heightmap.width; i++)
        {
            sumBottom += m_Vertices[0 * m_Heightmap.width + i].m_Position.y;
        }
        for (int i = 0; i < m_Heightmap.width; i++)
        {
            sumTop += m_Vertices[m_Heightmap.height - 1 * m_Heightmap.width + i].m_Position.y;
        }

        sumBottom = sumBottom / m_Heightmap.width;
        sumTop = sumTop / m_Heightmap.width;

        for (int i = 0; i < m_Heightmap.height; i++)
        {
            for (int j = 0; j < m_Heightmap.width; j++)
            {
              /*  if (i == 0)
                {
                    m_Vertices[i * m_Heightmap.width + j].m_Position = new Vector3(


                       Mathf.Cos(startTeta + j * stepTeta) * Mathf.Cos(startPhi + i * stepPhi) * (m_Radius + m_Vertices[i * m_Heightmap.width + j].m_Position.y),
                       Mathf.Sin(startPhi + i * stepPhi) * (m_Radius + sumBottom),
                       Mathf.Sin(startTeta + j * stepTeta) * Mathf.Cos(startPhi + i * stepPhi) * (m_Radius + sumBottom)
                       )
                       ;
                }
                else if (i == m_Heightmap.height - 1)
                {
                    m_Vertices[i * m_Heightmap.width + j].m_Position = new Vector3(


                       Mathf.Cos(startTeta + j * stepTeta) * Mathf.Cos(startPhi + i * stepPhi) * (m_Radius + sumTop),
                       Mathf.Sin(startPhi + i * stepPhi) * (m_Radius + sumTop),
                       Mathf.Sin(startTeta + j * stepTeta) * Mathf.Cos(startPhi + i * stepPhi) * (m_Radius + sumTop)
                       )
                       ;
                }*/
              //  else
               // {
                    //(m_Radius + m_Vertices[i * m_Heightmap.width].m_Position.y),
                    m_Vertices[i * m_Heightmap.width + j].m_Position = new Vector3(


                        Mathf.Cos(startTeta + j * stepTeta) * Mathf.Cos(startPhi + i * stepPhi) * (m_Radius + (m_Vertices[i * m_Heightmap.width + j].m_Position.y)*(Mathf.Pow(1.0f-(Mathf.Abs(Mathf.Sin(startPhi + i * stepPhi))),m_pow))),
                        Mathf.Sin(startPhi + i * stepPhi) * (m_Radius + (m_Vertices[i * m_Heightmap.width + j].m_Position.y)*(Mathf.Pow(1.0f-(Mathf.Abs(Mathf.Sin(startPhi + i * stepPhi))),m_pow))),
                        Mathf.Sin(startTeta + j * stepTeta) * Mathf.Cos(startPhi + i * stepPhi) * (m_Radius + (m_Vertices[i * m_Heightmap.width + j].m_Position.y) * (Mathf.Pow(1.0f - (Mathf.Abs(Mathf.Sin(startPhi + i * stepPhi))), m_pow)))
                        )
                        ;

               // }
                    

            }
        }

    }


}
